package com.zdsx.utils;

import org.apache.commons.lang3.StringUtils;

import java.nio.ByteBuffer;
import java.util.zip.CRC32;

//@SpringBootTest
class HexMessageUtils {
    private static final byte START_BYTE = (byte) 0x80;
    private static final byte END_BYTE = (byte) 0x81;
    private static final byte PROTOCOL_VERSION = (byte) 0x01;


    void codingHex() {
        // 示例输入
        byte serviceCode = (byte) 0x01; // 业务编码
        byte functionCode = (byte) 0x11; // 功能编码
        byte instructionType = (byte) 0x02; // 包类型：0x01:请求包，0x02:应答包
        byte executionResult = (byte) 0x01; // 执行结果：0x01：成功，其他：失败


        byte[] messageBody = getBody();

        // 生成报文
        byte[] message = buildMessage(serviceCode, functionCode, instructionType, executionResult, messageBody);

        // 输出报文
        String msgStr = bytesToHex(message);
        System.out.println(msgStr);

        decodeHex(msgStr);
    }

    public byte[] getBody(){
        // 首页请求，报文体为空
//        byte[] messageBody = new byte[]{}; // 报文体
        // 首页应答报文体
        byte[] messageBody = new byte[]{
                0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, // 产品编码：10字节
                0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, // 硬件版本号：10字节
                0x33, 0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33, // 软件版本号：10字节
                0x01, // LORA无线探测器功能：1字节， 0x01:允许，0x00：禁止
                0x7F, // LORA无线探测器数量：1字节，
                0x00, // 开关量采集功能：1字节，0x01:允许，0x00：禁止
                0x01, // 模拟量采集功能：1字节，0x01:允许，0x00：禁止
                0x01, // 当前模式：1字节，0x01:4-20mA，0x02：0-5V
                (byte) 0x19, 0x20, // RL值：2字节
                0x01, // RS485数据功能：1字节，0x01:允许，0x00：禁止
                0x03, // RS485数据模式：1字节，0x01:采集模式 0x02:监听模式  0x03:上传模式
                0x11, // RS485当前设置探测器数量：1字节
                0x00, // 无线数据上传平台功能：1字节，0x01:允许，0x00：禁止
                0x01, // 无线信号强度：1字节，0x01:允许，0x00：禁止
        }; // 报文体

        // 2.4.2.1 获取当前已经配置了无线探测的数据 请求报文体 空的
//        byte[] messageBody = new byte[]{}; // 报文体

        // 2.4.2.1 获取当前已经配置了无线探测的数据 应答报文体
//        byte[] messageBody = new byte[]{
//                0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x18 //16字节
//        }; // 报文体

        // 2.4.2.2根据探测器地址获取探测器属性 请求报文体
//        byte[] messageBody = new byte[]{
//                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x38 //16字节
//        }; // 报文体

        // 2.4.2.2根据探测器地址获取探测器属性 应答报文体
//        byte[] messageBody = new byte[]{
//                0x24,0x05,0x06,0x00,0x01, // 探测器编号：5字节
//                0x12, // 探测器地址 1-128：1字节
//                0x22, // 已添加探测器的浓度:1字节
//                0x02, // 已添加探测器的状态：1字节，0x00:正常 0x01:故障 0x02:报警
//                0x12, // 已添加探测器的电量：1字节
//                0x17, // 已添加探测器RSSI值：1字节
//        }; // 报文体


        // 2.4.2.3获取可添加的探测器地址 请求
        // byte[] messageBody = new byte[]{}; // 报文体

        // 2.4.2.3获取可添加的探测器地址 应答
//        byte[] messageBody = new byte[]{
//                0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x18 //16字节
//        }; // 报文体

        // 2.4.2.4添加/删除无线探测器 请求：bit=1为添加 bit=0为删除
//        byte[] messageBody = new byte[]{
//                0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x18 //16字节
//        }; // 报文体



        // 2.4.5.1 获取RS485采集配置数据 请求
//        byte[] messageBody = new byte[]{}; // 报文体
        // 2.4.5.1 获取RS485采集配置数据 应答
//        byte[] messageBody = new byte[]{
//                0x01,// 1	是否启用该功能	HEX	1	0x01:启用 0x00:禁用
//                0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x18 // 2	配置了RS486采集的探头	HEX	16	0x00:禁用 0x01:启用
//        }; // 报文体

        // 2.4.5.2 根据探测器地址获取探测器属性 请求
//        byte[] messageBody = new byte[]{
//                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 // 2	配置了RS486采集的探头	HEX	16	0x00:禁用 0x01:启用
//        }; // 报文体
        // 2.4.5.2 根据探测器地址获取探测器属性 应答
//        byte[] messageBody = new byte[]{
//                0x01,// 1	探测器地址	HEX	1	转十进制
//                0x01,// 2	是否允许	HEX	1	0x01:允许，0x00：禁止
//                0x01,// 3	浓度	HEX	1	转十进制
//                0x01,// 4	状态	HEX	1	0x00:正常 0x01:故障 0x02:报警
//                0x11 // 5	当前配置的故障判断时间	HEX	1	转十进制
//        }; // 报文体



        // 2.4.5.3 批量探测器允许/禁止 请求
//        byte[] messageBody = new byte[]{
//
//        }; // 报文体
        // 2.4.5.2 根据探测器地址获取探测器属性 应答
//        byte[] messageBody = new byte[]{
//                0x01,// 1	探测器地址	HEX	1	转十进制
//                0x01,// 2	是否允许	HEX	1	0x01:允许，0x00：禁止
//                0x01,// 3	浓度	HEX	1	转十进制
//                0x01,// 4	状态	HEX	1	0x00:正常 0x01:故障 0x02:报警
//                0x11 // 5	当前配置的故障判断时间	HEX	1	转十进制
//        }; // 报文体



        // 2.4.5.4 配置探测器故障判断时间 请求
//        byte[] messageBody = new byte[]{
//                0x01, // 1	探测器地址	HEX	1	转十进制
//                0x11 // 2   故障判断时间 HEX	1	转十进制  单位是分钟
//        }; // 报文体
        // 2.4.5.4 配置探测器故障判断时间 应答
//        byte[] messageBody = new byte[]{}; // 报文体


        // 2.4.5.5 探测器调零 请求
//        byte[] messageBody = new byte[]{
//                0x61, // 1	探测器地址	HEX	1	转十进制
//        }; // 报文体
        // 2.4.5.5 探测器调零 应答
//        byte[] messageBody = new byte[]{}; // 报文体

        // 2.4.5.6 探测器校准 请求
//        byte[] messageBody = new byte[]{
//                0x61, // 1	探测器地址	HEX	1	转十进制
//                0x17, // 2	浓度	HEX	1	转十进制
//        }; // 报文体
        // 2.4.5.6 探测器校准 应答
//        byte[] messageBody = new byte[]{}; // 报文体


        // 2.4.4.1 获取4-20mA/0-5V采集配置 请求
//        byte[] messageBody = new byte[]{}; // 报文体
        // 2.4.4.1 获取4-20mA/0-5V采集配置 应答
//        byte[] messageBody = new byte[]{
//            0x01, // 1  当前模式 HEX  1   0x01:4-20mA   0x02:0-5V
//            0x21, // 2  当前RL值 HEX  1   转十进制
//
//            0x00, // 3  信号1允许/禁止 HEX  1   0x01:允许  0x00:禁止
//            0x31, // 4  信号1地址 HEX  1   转十进制
//            0x41, // 5  信号1浓度 HEX  1   转十进制
//            0x01, // 6  信号1状态 HEX  1   正常：0x00 故障：0x03 低报：0x01 高报：0x02
//
//            0x01, // 7  信号2允许/禁止 HEX  1   0x01:允许  0x00:禁止
//            0x58, // 8  信号2地址 HEX  1   转十进制
//            0x22, // 9  信号2浓度 HEX  1   转十进制
//            0x02, // 10  信号2状态 HEX  1   正常：0x00 故障：0x03 低报：0x01 高报：0x02
//        }; // 报文体



        // 2.4.4.2 设置4-20mA/0-5V采集模式 请求
//        byte[] messageBody = new byte[]{
//                0x01 // 1	设置的采集模式	 HEX	1	0x01:4-20mA 0x02:0-5V
//        }; // 报文体
        // 2.4.4.2 设置4-20mA/0-5V采集模式 应答
//        byte[] messageBody = new byte[]{}; // 报文体



        // 2.4.4.3 设置信号1/2 允许/禁止 请求
//        byte[] messageBody = new byte[]{
//                0x31, // 1	信号1/2探头地址	HEX	1	转十进制
//                0x00  // 2	允许/禁止采集	HEX	1	0x01:允许  0x00:禁止
//        }; // 报文体
        // 2.4.4.3 设置信号1/2 允许/禁止 应答
//        byte[] messageBody = new byte[]{}; // 报文体


        // 2.4.3.1 获取开关量采集配置  请求
//        byte[] messageBody = new byte[]{}; // 报文体
        // 2.4.3.1 获取开关量采集配置  应答
//        byte[] messageBody = new byte[]{
//                0x16, // 1	当前开关量采集信号地址1	HEX	1
//                0x01, // 2	地址1开关量采集状态	HEX		0x01:允许 0x00:禁止
//                0x41, // 3	当前开关量采集信号地址2	HEX	1
//                0x01, // 4	地址2开关量采集状态	HEX	1	0x01:允许 0x00:禁止
//                0x01, // 5	开关量采集允许/禁止	HEX	1
//        }; // 报文体

        // 2.4.3.2 设置开关量采集配置允许/禁止  请求
//        byte[] messageBody = new byte[]{
//                0x01 // 1  允许/禁止  HEX   1   0x01:允许  0x00:禁止
//        }; // 报文体
        // 2.4.3.2 设置开关量采集配置允许/禁止  应答
//        byte[] messageBody = new byte[]{}; // 报文体



        // 2.4.7.1 获取RS485上传配置 请求
//        byte[] messageBody = new byte[]{}; // 报文体
        // 2.4.7.1 获取RS485上传配置 应答
//        byte[] messageBody = new byte[]{
//                0x71,// 1	探测器数量	HEX	1	0x01:启用 0x00:禁用
//                0x01,// 2	当前是否启用该功能	HEX	1	0x00:禁用 0x01:启用
//        }; // 报文体


        // 2.4.7.2 设置RS485是否启用上传 请求
//        byte[] messageBody = new byte[]{
//                0x00,// 1 是否启用该功能	HEX	1	0x00:禁用 0x01:启用
//        }; // 报文体
        // 2.4.7.2 设置RS485是否启用上传 应答
//        byte[] messageBody = new byte[]{}; // 报文体


        // 2.4.8.1 查询云上传配置 请求
//        byte[] messageBody = new byte[]{}; // 报文体
        // 2.4.8.1 查询云上传配置 应答
//        byte[] messageBody = new byte[]{
//                0x01,// 1	是否启用该功能	HEX	1	0x01:启用 0x00:禁用
//                0x01,// 2	主机模式/从机模式	HEX	1	0x01:主机模式 0x00:从机模式
//                0x00,// 3	上传最大探测器/上传所有探测器	HEX	1	0x01:上传最大 0x00:上传所有
//                0x26,// 4	心跳时间	HEX	1	转10进制
//                0x18,// 5	当前信号值	HEX	1	转10进制
//        }; // 报文体


        // 2.4.8.2 设置云上传配置-是否启用 请求
//        byte[] messageBody = new byte[]{
//                0x01 // 1	是否启用	HEX	1	0x00：禁用   0x01：启用
//        }; // 报文体
        // 2.4.8.2 设置云上传配置-是否启用 应答
//        byte[] messageBody = new byte[]{}; // 报文体


        // 2.4.8.3 设置云上传配置-主机/从机 请求
//        byte[] messageBody = new byte[]{
//                0x01 // 1	主机/从机	HEX	1	 0x01:主机模式 0x00:从机模式
//        }; // 报文体
        // 2.4.8.3 设置云上传配置-主机/从机 应答
//        byte[] messageBody = new byte[]{}; // 报文体


        // 2.4.8.4 设置云上传配置-最大探测器/所有探测器 请求
//        byte[] messageBody = new byte[]{
//                0x01 // 1	最大探测器/所有探测器	HEX	1  0x01:上传最大  0x00:上传所有
//        }; // 报文体
        // 2.4.8.4 设置云上传配置-最大探测器/所有探测器 应答
//        byte[] messageBody = new byte[]{}; // 报文体


        return messageBody;
    }

    private static byte[] buildMessage(byte serviceCode, byte functionCode, byte instructionType, byte executionResult, byte[] messageBody) {
        // 报文长度 = 起始符(1) + 报文长度(2) + 协议版本号(1) + 业务编码(1) + 功能编码(1) + 指令类型(1) + 执行结果(1) + 报文体长度(2) + 报文体(N) + 校验码(2) + 结束符(1)
        int messageLength = 1 + 2 + 1 + 1 + 1 + 1 + 1 + 2 + messageBody.length + 2 + 1;
        ByteBuffer buffer = ByteBuffer.allocate(messageLength);

        buffer.put(START_BYTE); // 起始符
        buffer.putShort((short) (messageLength)); // 报文长度 (包括起始符、报文长度字段和结束符)
        buffer.put(PROTOCOL_VERSION); // 协议版本号
        buffer.put(serviceCode); // 业务编码
        buffer.put(functionCode); // 功能编码
        buffer.put(instructionType); // 指令类型
        buffer.put(executionResult); // 执行结果
        buffer.putShort((short) messageBody.length); // 报文体长度
        buffer.put(messageBody); // 报文体

        // 计算CRC16校验码
        byte[] crcInput = new byte[buffer.position()];
        System.arraycopy(buffer.array(), 0, crcInput, 0, buffer.position());
//        short crc16 = calculateCRC16(crcInput);
        short tt = (short) crc16_1(crcInput);
        byte[] crc = Integer.toHexString(tt).getBytes();
        short a = 0x5C6A;
        buffer.putShort(tt); // 校验码

        buffer.put(END_BYTE); // 结束符

        return buffer.array();
    }

    public static int crc16_1(final byte[] buffer) {
        int crc = 0xFFFF; // 初始值
        for (byte b : buffer) {
//            crc ^= b << 8; // 将字节左移8位，与CRC寄存器异或
            crc ^= (b & 0xFF) << 8; // 将byte视为无符号数并左移8位
            for (int i = 0; i < 8; i++) {
                if ((crc & 0x8000) != 0) { // 如果最高位（第15位）为1
                    crc = (crc << 1) ^ 0x1021; // 左移一位，与多项式异或
                } else {
                    crc = crc << 1; // 只需左移一位
                }
            }
        }
        return crc & 0xFFFF; // 返回16位CRC值
    }
    public static String crc16(final byte[] buffer) {
        int crc = 0xFFFF; // 初始值
        for (byte b : buffer) {
            crc ^= b << 8; // 将字节左移8位，与CRC寄存器异或
            for (int i = 0; i < 8; i++) {
                if ((crc & 0x8000) != 0) { // 如果最高位（第15位）为1
                    crc = (crc << 1) ^ 0x1021; // 左移一位，与多项式异或
                } else {
                    crc = crc << 1; // 只需左移一位
                }
            }
        }
        crc &= 0xFFFF; // 确保CRC值在0-FFFF之间
        return String.format("%04X", crc); // 返回16位CRC值的16进制字符串
    }

    private static short calculateCRC16(byte[] data) {
        // 这里需要实现CRC16的计算，可以使用现有的库或自己实现CRC16算法
        // 以下代码仅为示例，需要替换为正确的CRC16计算方法
        CRC32 crc32 = new CRC32();
        crc32.update(data);
        return (short) (crc32.getValue() & 0xFFFF);
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }


    void decodeHex(String hexString) {
        if (StringUtils.isBlank(hexString)) {
            // 示例输入的十六进制字符串
            hexString = "80 00 0E 01 02 03 04 05 00 04 DE AD BE EF 29 B6 81";
        }

        // 将十六进制字符串转换为字节数组
        byte[] message = hexStringToByteArray(hexString.replaceAll(" ", ""));

        // 解码报文
        decodeMessage(message);
    }

    private static void decodeMessage(byte[] message) {
        ByteBuffer buffer = ByteBuffer.wrap(message);

        byte startByte = buffer.get(); // 起始符
        short messageLength = buffer.getShort(); // 报文长度
        byte protocolVersion = buffer.get(); // 协议版本号
        byte serviceCode = buffer.get(); // 业务编码
        byte functionCode = buffer.get(); // 功能编码
        byte instructionType = buffer.get(); // 指令类型
        byte executionResult = buffer.get(); // 执行结果
        short bodyLength = buffer.getShort(); // 报文体长度

        byte[] messageBody = new byte[bodyLength];
        buffer.get(messageBody); // 报文体

        short crc16 = buffer.getShort(); // 校验码
        byte endByte = buffer.get(); // 结束符

        // 计算CRC16校验码
        byte[] crcInput = new byte[buffer.position() - 3]; // 不包括校验码和结束符
        System.arraycopy(message, 0, crcInput, 0, buffer.position() - 3);
//        short calculatedCrc16 = calculateCRC16(crcInput);
        short calculatedCrc16 = (short) crc16_1(crcInput);

        // 校验CRC16
        boolean crcCheck = crc16 == calculatedCrc16;
        System.out.println("校验码是否正确: " + crcCheck);


        // 打印解码后的报文各部分
        System.out.println("起始符: " + String.format("%02X", startByte));
        System.out.println("报文长度: " + messageLength);
        System.out.println("协议版本号: " + String.format("%02X", protocolVersion));
        System.out.println("业务编码: " + String.format("%02X", serviceCode));
        System.out.println("功能编码: " + String.format("%02X", functionCode));
        System.out.println("指令类型: " + String.format("%02X", instructionType));
        System.out.println("执行结果: " + String.format("%02X", executionResult));
        System.out.println("报文体长度: " + bodyLength);
        System.out.println("报文体: " + bytesToHex(messageBody));
        System.out.println("校验码: " + String.format("%04X", crc16));
        System.out.println("结束符: " + String.format("%02X", endByte));
    }

    private static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }


}